﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using XLua;

public class TimerUtil : IUtil
{
    private TaskTimerCore timer;
    public void Init()
    {
        timer = new TaskTimerCore();
        Debug.Log("timer inited!");
    }

    public void Tick()
    {
        timer.TickTaskTimer();
    }

    public void Uninit()
    {
        timer = null;
        Debug.Log("timer uninited!");
    }
    [LuaCallCSharp]
    public int AddTaskTimer(
          float delay
        , float interval
        , Action<int> taskCallback
        , int count = 1
        , Action<int> cancelCallback = null)
    {
        return timer.AddTaskTimer(delay, interval, taskCallback, cancelCallback, count);
    }
    [LuaCallCSharp]
    public bool DeleteTaskTimer(int tid)
    {
        return timer.DeleteTaskTimer(tid);
    }
}

/// <summary>
/// 公共定时工具类
/// </summary>
public class TaskTimerCore
{
    private class TaskTimerPack
    {
        public int tid;
        public Action<int> callback;
        public TaskTimerPack(int tid, Action<int> callback)
        {
            this.tid = tid;
            this.callback = callback;
        }
    }
    // 一条定时任务类
    private class TaskTimerCell
    {
        public int tid;
        public float delay;
        public float interval;
        public int count;
        public double startTime;
        public double targetTime;
        public Action<int> taskCallback;
        public Action<int> cancelCallback;
        public ulong loopIndex;
        public TaskTimerCell(int tid, float delay, float interval, int count, double startTime, double targetTime, Action<int> taskCallback, Action<int> cancelCallback)
        {
            this.tid = tid;
            this.delay = delay;
            this.interval = interval;
            this.count = count;
            this.startTime = startTime;
            this.targetTime = targetTime;
            this.taskCallback = taskCallback;
            this.cancelCallback = cancelCallback;
            this.loopIndex = 0uL;
        }
    }

    private const string TID_LOCK = "TickTimer_tidLock";
    private readonly DateTime UTCStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
    private readonly ConcurrentDictionary<int, TaskTimerCell> taskTimerDic;

    public TaskTimerCore()
    {
        taskTimerDic = new ConcurrentDictionary<int, TaskTimerCell>();
    }

    /// <summary>
    /// 添加一个定时任务
    /// </summary>
    /// <param name="delay"></param>
    /// <param name="taskCallback"></param>
    /// <param name="cancelCallback"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public int AddTaskTimer(float delay, float interval, Action<int> taskCallback, Action<int> cancelCallback, int count = 1)
    {
        int tid = GenerateTid();
        double utcSeconds = GetUTCSeconds();
        double targetTime = utcSeconds + (double)delay;
        TaskTimerCell timer = new TaskTimerCell(tid, delay, interval, count, utcSeconds, targetTime, taskCallback, cancelCallback);
        if (taskTimerDic.TryAdd(tid, timer))
        {
            return tid;
        }
        UnityEngine.Debug.LogWarning($"tid:{tid} already exsit!");
        return -1;
    }
    /// <summary>
    /// 移除一个正在进行的定时器
    /// </summary>
    /// <param name="tid"></param>
    /// <returns></returns>
    public bool DeleteTaskTimer(int tid)
    {
        if (taskTimerDic.TryRemove(tid, out TaskTimerCell timer))
        {
            timer.cancelCallback?.Invoke(tid);
            return true;
        }
        UnityEngine.Debug.LogWarning($"tid:{tid} remove failed!");
        return false;
    }

    /// <summary>
    /// 外部帧更新函数调用打点逻辑
    /// </summary>
    public void TickTaskTimer()
    {
        double utcSeconds = GetUTCSeconds();
        foreach (KeyValuePair<int, TaskTimerCell> item in taskTimerDic)
        {
            TaskTimerCell value = item.Value;
            if (utcSeconds < value.targetTime)
            {
                continue;
            }
            value.loopIndex++;
            if (value.count > 0)
            {
                value.count--;
                if (value.count == 0)
                {
                    void FinishTask(int tid)
                    {
                        if (taskTimerDic.TryRemove(tid, out var val1))
                        {
                            val1.taskCallback(tid);
                            val1.taskCallback = null;
                        }
                        else
                        {
                            UnityEngine.Debug.LogError($"remove task failed:{tid}");
                        }
                    }
                    FinishTask(value.tid);
                    continue;
                }
                // 定时还未结束，继续计算下一目标时间
                value.targetTime = value.startTime + (double)(value.delay + value.interval * value.loopIndex);
                value.taskCallback(value.tid);
            }
            else
            {
                value.targetTime = value.startTime + (double)(value.delay + value.interval * value.loopIndex);
                value.taskCallback(value.tid);
            }
        }
    }


    #region API Func
    double GetUTCSeconds()
    {
        return (DateTime.UtcNow - UTCStartTime).TotalSeconds;
    }
    int GenerateTid()
    {
        int tid = 0;
        lock (TID_LOCK)
        {
            do
            {
                tid++;
                if (tid == int.MaxValue)
                {
                    tid = 0;
                }
            }
            while (taskTimerDic.ContainsKey(tid));
            return tid;
        }
    }
    #endregion
}